﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace ProjectCastamere
{
    public static class InputManager
    {
        // enum for different actions available in the game
        // will be expanded as needed
        public enum GameActions
        {
            Ok,
            Back,
            ExitGame,
            CursorUp,
            CursorDown,
            TotalGameActionCount,
        }

        // A string that can be used to associate an action
        // useful to make a menu displaying the controls :)
        private static readonly string[] gameActionNames = 
        {
                "Ok",
                "Back",
                "Exit Game",
                "Move Cursor - Up",
                "Move Cursor - Down",
        };

        // Retrieves an actions name
        public static string GetActionName(GameActions gameActions)
        {
            int index = (int)gameActions;

            if ((index < 0) || (index > gameActionNames.Length))
            {
                throw new ArgumentException("gameActions");
            }

            return gameActionNames[index];
        }

        // An enum of buttons available on an xbox 360 controller
        public enum GamePadButtons
        {
            Start,
            Back,
            A,
            B,
            X,
            Y,
            Up,
            Down,
            Left,
            Right,
            LeftShoulder,
            RightShoulder,
            LeftTrigger,
            RightTrigger,
        }

        public class GameActionMap
        {
            /// <summary>
            /// List of GamePad controls to be mapped to a given action.
            /// </summary>
            public List<GamePadButtons> gamePadButtons = new List<GamePadButtons>();


            /// <summary>
            /// List of Keyboard controls to be mapped to a given action.
            /// </summary>
            public List<Keys> keyboardKeys = new List<Keys>();
        }

        // The limit when a analog stick triggers a keypress
        const float analogLimit = 0.5f;

        // The current and previous keystates
        private static KeyboardState currentKeyboardState;

        public static KeyboardState CurrentKeyboardState
        {
            get { return currentKeyboardState; }
        }

        private static KeyboardState previousKeyboardState;

        public static bool IsKeyPressed(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key);
        }

        // Determine if the current state is different from previous
        public static bool IsKeyTriggered(Keys key)
        {
            return (currentKeyboardState.IsKeyDown(key)) &&
                (!previousKeyboardState.IsKeyDown(key));
        }

        // Same stuff this time for the gamepad
        private static GamePadState currentGamePadState;

        public static GamePadState CurrentGamePadState
        {
            get { return currentGamePadState; }
        }

        private static GamePadState previousGamePadState;

        public static bool IsGamePadStartPressed()
        {
            return (currentGamePadState.Buttons.Start == ButtonState.Pressed);
        }

        public static bool IsGamePadBackPressed()
        {
            return (currentGamePadState.Buttons.Back == ButtonState.Pressed);
        }

        public static bool IsGamePadAPressed()
        {
            return (currentGamePadState.Buttons.A == ButtonState.Pressed);
        }

        public static bool IsGamePadBPressed()
        {
            return (currentGamePadState.Buttons.B == ButtonState.Pressed);
        }

        public static bool IsGamePadXPressed()
        {
            return (currentGamePadState.Buttons.X == ButtonState.Pressed);
        }

        public static bool IsGamePadYPressed()
        {
            return (currentGamePadState.Buttons.Y == ButtonState.Pressed);
        }

        public static bool IsGamePadLeftShoulderPressed()
        {
            return (currentGamePadState.Buttons.LeftShoulder == ButtonState.Pressed);
        }

        public static bool IsGamePadRightShoulderPressed()
        {
            return (currentGamePadState.Buttons.RightShoulder == ButtonState.Pressed);
        }

        public static bool IsGamePadDPadUpPressed()
        {
            return (currentGamePadState.DPad.Up == ButtonState.Pressed);
        }

        public static bool IsGamePadDPadDownPressed()
        {
            return (currentGamePadState.DPad.Down == ButtonState.Pressed);
        }

        public static bool IsGamePadDPadLeftPressed()
        {
            return (currentGamePadState.DPad.Left == ButtonState.Pressed);
        }

        public static bool IsGamePadDPadRightPressed()
        {
            return (currentGamePadState.DPad.Right == ButtonState.Pressed);
        }

        // Check if the gamepad's left trigger is pressed.
        public static bool IsGamePadLeftTriggerPressed()
        {
            return (currentGamePadState.Triggers.Left > analogLimit);
        }

        // Check if the gamepad's right trigger is pressed.
        public static bool IsGamePadRightTriggerPressed()
        {
            return (currentGamePadState.Triggers.Right > analogLimit);
        }

        // Check if Up on the gamepad's left analog stick is pressed.
        public static bool IsGamePadLeftStickUpPressed()
        {
            return (currentGamePadState.ThumbSticks.Left.Y > analogLimit);
        }

        // Check if Down on the gamepad's left analog stick is pressed.
        public static bool IsGamePadLeftStickDownPressed()
        {
            return (-1f * currentGamePadState.ThumbSticks.Left.Y > analogLimit);
        }

        // Check if Left on the gamepad's left analog stick is pressed.
        public static bool IsGamePadLeftStickLeftPressed()
        {
            return (-1f * currentGamePadState.ThumbSticks.Left.X > analogLimit);
        }

        // Check if Right on the gamepad's left analog stick is pressed.
        public static bool IsGamePadLeftStickRightPressed()
        {
            return (currentGamePadState.ThumbSticks.Left.X > analogLimit);
        }

        // Check if the GamePadKey value specified is pressed.
        private static bool IsGamePadButtonPressed(GamePadButtons gamePadKey)
        {
            switch (gamePadKey)
            {
                case GamePadButtons.Start:
                    return IsGamePadStartPressed();

                case GamePadButtons.Back:
                    return IsGamePadBackPressed();

                case GamePadButtons.A:
                    return IsGamePadAPressed();

                case GamePadButtons.B:
                    return IsGamePadBPressed();

                case GamePadButtons.X:
                    return IsGamePadXPressed();

                case GamePadButtons.Y:
                    return IsGamePadYPressed();

                case GamePadButtons.LeftShoulder:
                    return IsGamePadLeftShoulderPressed();

                case GamePadButtons.RightShoulder:
                    return IsGamePadRightShoulderPressed();

                case GamePadButtons.LeftTrigger:
                    return IsGamePadLeftTriggerPressed();

                case GamePadButtons.RightTrigger:
                    return IsGamePadRightTriggerPressed();

                case GamePadButtons.Up:
                    return IsGamePadDPadUpPressed() ||
                        IsGamePadLeftStickUpPressed();

                case GamePadButtons.Down:
                    return IsGamePadDPadDownPressed() ||
                        IsGamePadLeftStickDownPressed();

                case GamePadButtons.Left:
                    return IsGamePadDPadLeftPressed() ||
                        IsGamePadLeftStickLeftPressed();

                case GamePadButtons.Right:
                    return IsGamePadDPadRightPressed() ||
                        IsGamePadLeftStickRightPressed();
            }

            return false;
        }

        // Check if the gamepad's Start button was just pressed.
        public static bool IsGamePadStartTriggered()
        {
            return ((currentGamePadState.Buttons.Start == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.Start == ButtonState.Released));
        }

        // Check if the gamepad's Back button was just pressed.
        public static bool IsGamePadBackTriggered()
        {
            return ((currentGamePadState.Buttons.Back == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.Back == ButtonState.Released));
        }

        // Check if the gamepad's A button was just pressed.
        public static bool IsGamePadATriggered()
        {
            return ((currentGamePadState.Buttons.A == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.A == ButtonState.Released));
        }

        // Check if the gamepad's B button was just pressed.
        public static bool IsGamePadBTriggered()
        {
            return ((currentGamePadState.Buttons.B == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.B == ButtonState.Released));
        }

        // Check if the gamepad's X button was just pressed.
        public static bool IsGamePadXTriggered()
        {
            return ((currentGamePadState.Buttons.X == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.X == ButtonState.Released));
        }

        // Check if the gamepad's Y button was just pressed.
        public static bool IsGamePadYTriggered()
        {
            return ((currentGamePadState.Buttons.Y == ButtonState.Pressed) &&
              (previousGamePadState.Buttons.Y == ButtonState.Released));
        }

        // Check if the gamepad's LeftShoulder button was just pressed.
        public static bool IsGamePadLeftShoulderTriggered()
        {
            return (
                (currentGamePadState.Buttons.LeftShoulder == ButtonState.Pressed) &&
                (previousGamePadState.Buttons.LeftShoulder == ButtonState.Released));
        }

        // Check if the gamepad's RightShoulder button was just pressed.
        public static bool IsGamePadRightShoulderTriggered()
        {
            return (
                (currentGamePadState.Buttons.RightShoulder == ButtonState.Pressed) &&
                (previousGamePadState.Buttons.RightShoulder == ButtonState.Released));
        }

        // Check if Up on the gamepad's directional pad was just pressed.
        public static bool IsGamePadDPadUpTriggered()
        {
            return ((currentGamePadState.DPad.Up == ButtonState.Pressed) &&
              (previousGamePadState.DPad.Up == ButtonState.Released));
        }

        // Check if Down on the gamepad's directional pad was just pressed.
        public static bool IsGamePadDPadDownTriggered()
        {
            return ((currentGamePadState.DPad.Down == ButtonState.Pressed) &&
              (previousGamePadState.DPad.Down == ButtonState.Released));
        }

        // Check if Left on the gamepad's directional pad was just pressed.
        public static bool IsGamePadDPadLeftTriggered()
        {
            return ((currentGamePadState.DPad.Left == ButtonState.Pressed) &&
              (previousGamePadState.DPad.Left == ButtonState.Released));
        }

        // Check if Right on the gamepad's directional pad was just pressed.
        public static bool IsGamePadDPadRightTriggered()
        {
            return ((currentGamePadState.DPad.Right == ButtonState.Pressed) &&
              (previousGamePadState.DPad.Right == ButtonState.Released));
        }

        // Check if the gamepad's left trigger was just pressed.
        public static bool IsGamePadLeftTriggerTriggered()
        {
            return ((currentGamePadState.Triggers.Left > analogLimit) &&
                (previousGamePadState.Triggers.Left < analogLimit));
        }

        // Check if the gamepad's right trigger was just pressed.
        public static bool IsGamePadRightTriggerTriggered()
        {
            return ((currentGamePadState.Triggers.Right > analogLimit) &&
                (previousGamePadState.Triggers.Right < analogLimit));
        }

        // Check if Up on the gamepad's left analog stick was just pressed.
        public static bool IsGamePadLeftStickUpTriggered()
        {
            return ((currentGamePadState.ThumbSticks.Left.Y > analogLimit) &&
                (previousGamePadState.ThumbSticks.Left.Y < analogLimit));
        }

        // Check if Down on the gamepad's left analog stick was just pressed.
        public static bool IsGamePadLeftStickDownTriggered()
        {
            return ((-1f * currentGamePadState.ThumbSticks.Left.Y > analogLimit) &&
                (-1f * previousGamePadState.ThumbSticks.Left.Y < analogLimit));
        }

        // Check if Left on the gamepad's left analog stick was just pressed.
        public static bool IsGamePadLeftStickLeftTriggered()
        {
            return ((-1f * currentGamePadState.ThumbSticks.Left.X > analogLimit) &&
                (-1f * previousGamePadState.ThumbSticks.Left.X < analogLimit));
        }

        // Check if Right on the gamepad's left analog stick was just pressed.
        public static bool IsGamePadLeftStickRightTriggered()
        {
            return ((currentGamePadState.ThumbSticks.Left.X > analogLimit) &&
                (previousGamePadState.ThumbSticks.Left.X < analogLimit));
        }

        // Check if the GamePadKey value specified was pressed this frame.
        private static bool IsGamePadButtonTriggered(GamePadButtons gamePadKey)
        {
            switch (gamePadKey)
            {
                case GamePadButtons.Start:
                    return IsGamePadStartTriggered();

                case GamePadButtons.Back:
                    return IsGamePadBackTriggered();

                case GamePadButtons.A:
                    return IsGamePadATriggered();

                case GamePadButtons.B:
                    return IsGamePadBTriggered();

                case GamePadButtons.X:
                    return IsGamePadXTriggered();

                case GamePadButtons.Y:
                    return IsGamePadYTriggered();

                case GamePadButtons.LeftShoulder:
                    return IsGamePadLeftShoulderTriggered();

                case GamePadButtons.RightShoulder:
                    return IsGamePadRightShoulderTriggered();

                case GamePadButtons.LeftTrigger:
                    return IsGamePadLeftTriggerTriggered();

                case GamePadButtons.RightTrigger:
                    return IsGamePadRightTriggerTriggered();

                case GamePadButtons.Up:
                    return IsGamePadDPadUpTriggered() ||
                        IsGamePadLeftStickUpTriggered();

                case GamePadButtons.Down:
                    return IsGamePadDPadDownTriggered() ||
                        IsGamePadLeftStickDownTriggered();

                case GamePadButtons.Left:
                    return IsGamePadDPadLeftTriggered() ||
                        IsGamePadLeftStickLeftTriggered();

                case GamePadButtons.Right:
                    return IsGamePadDPadRightTriggered() ||
                        IsGamePadLeftStickRightTriggered();
            }

            return false;
        }

        // The action mappings for the game.
        private static GameActionMap[] gameActionMaps;


        public static GameActionMap[] GameActionMaps
        {
            get { return gameActionMaps; }
        }


        // Reset the action maps to their default values.
        private static void ResetActionMaps()
        {
            // the total number of mapped game actions
            gameActionMaps = new GameActionMap[(int)GameActions.TotalGameActionCount];

            // If the user presses enter or the A button xbox
            gameActionMaps[(int)GameActions.Ok] = new GameActionMap();
            gameActionMaps[(int)GameActions.Ok].keyboardKeys.Add(Keys.Enter);
            gameActionMaps[(int)GameActions.Ok].gamePadButtons.Add(GamePadButtons.A);

            // If the user presses escape or the b button xbox
            gameActionMaps[(int)GameActions.Back] = new GameActionMap();
            gameActionMaps[(int)GameActions.Back].keyboardKeys.Add(Keys.Escape);
            gameActionMaps[(int)GameActions.Back].gamePadButtons.Add(GamePadButtons.B);

            // If the user presses escape or the back button xbox
            gameActionMaps[(int)GameActions.ExitGame] = new GameActionMap();
            gameActionMaps[(int)GameActions.ExitGame].keyboardKeys.Add(Keys.Escape);
            gameActionMaps[(int)GameActions.ExitGame].gamePadButtons.Add(GamePadButtons.Back);

            // If the user presses up/w or down on the analog or dpad button xbox
            gameActionMaps[(int)GameActions.CursorUp] = new GameActionMap();
            gameActionMaps[(int)GameActions.CursorUp].keyboardKeys.Add(Keys.Up);
            gameActionMaps[(int)GameActions.CursorUp].keyboardKeys.Add(Keys.W);
            gameActionMaps[(int)GameActions.CursorUp].gamePadButtons.Add(GamePadButtons.Up);

            // If the user presses down/s or down on the analog or dpad button xbox
            gameActionMaps[(int)GameActions.CursorDown] = new GameActionMap();
            gameActionMaps[(int)GameActions.CursorDown].keyboardKeys.Add(Keys.Down);
            gameActionMaps[(int)GameActions.CursorDown].keyboardKeys.Add(Keys.S);
            gameActionMaps[(int)GameActions.CursorDown].gamePadButtons.Add(GamePadButtons.Down);
        }

        // Check if an action has been pressed.
        public static bool IsActionPressed(GameActions action)
        {
            return IsActionMapPressed(gameActionMaps[(int)action]);
        }

        // Check if an action was just performed in the most recent update.
        public static bool IsActionTriggered(GameActions action)
        {
            return IsActionMapTriggered(gameActionMaps[(int)action]);
        }


        // Check if an action map has been pressed.
        private static bool IsActionMapPressed(GameActionMap gameActionMap)
        {
            for (int i = 0; i < gameActionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyPressed(gameActionMap.keyboardKeys[i]))
                {
                    return true;
                }
            }
            if (currentGamePadState.IsConnected)
            {
                for (int i = 0; i < gameActionMap.gamePadButtons.Count; i++)
                {
                    if (IsGamePadButtonPressed(gameActionMap.gamePadButtons[i]))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        // Check if an action map has been triggered this frame.
        private static bool IsActionMapTriggered(GameActionMap gameActionMap)
        {
            for (int i = 0; i < gameActionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyTriggered(gameActionMap.keyboardKeys[i]))
                {
                    return true;
                }
            }
            if (currentGamePadState.IsConnected)
            {
                for (int i = 0; i < gameActionMap.gamePadButtons.Count; i++)
                {
                    if (IsGamePadButtonTriggered(gameActionMap.gamePadButtons[i]))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        // Initializes the default control keys for all actions.
        public static void Initialize()
        {
            ResetActionMaps();
        }

        // Updates the keyboard and gamepad control states.
        public static void Update()
        {
            // update the keyboard state
            previousKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            // update the gamepad state
            previousGamePadState = currentGamePadState;
            currentGamePadState = GamePad.GetState(PlayerIndex.One);
        }
    }
}
